home *** CD-ROM | disk | FTP | other *** search
- /*
- * Sequent Balance Serial Line IP Driver
- *
- * This driver is a modified version of the 7.1 (Berkeley) version. The
- * original code was developed by Rick Adams and subsequently modified
- * by Chris Torek for 4.3BSD. Other changes made at Berkeley based in
- * part on code by Kirk Smith. The Sequent port was done by Phil Klimbal.
- *
- * Authors Addresses:
- * Rick Adams Chris Torek
- * rick@seismo.css.gov chris@mimsy.umd.edu
- * seismo!rick umcp-cs!chris
- *
- * Kirk Smith Phil Klimbal
- * ks@ecn.purdue.edu klimbal@riacs.edu
- * pur-ee!ks riacs!klimbal
- *
- * Copyright (C) 1987 Research Institute for Advanced Computer Science.
- * All rights reserved. The RIACS Software Policy contains specific
- * terms and conditions on the use of this software, and must be
- * distributed with any copies. This file may be redistributed. This
- * copyright and notice must be preserved in all copies made of this file.
- */
-
- #include "../h/param.h"
- #include "../h/mutex.h"
- #include "../machine/gate.h"
- #include "../balance/engine.h"
- #include "../h/vmmeter.h" /* for plocal.h */
- #include "../h/vmsystm.h" /* for plocal.h */
- #include "../machine/pte.h" /* for plocal.h */
- #include "../machine/vmparam.h" /* for plocal.h */
- #include "../machine/plocal.h"
- #include "../machine/intctl.h"
- #include "../h/time.h"
- #include "../h/mbuf.h"
- #include "../h/buf.h"
- #include "../h/dk.h"
- #include "../h/socket.h"
- #include "../h/ioctl.h"
- #include "../h/file.h"
- #include "../h/tty.h"
- #include "../h/errno.h"
-
- #include "../net/if.h"
- #include "../net/netisr.h"
- #include "../net/route.h"
- #include "../net/if_sl.h"
-
- #if INET
- #include "../netinet/in.h"
- #include "../netinet/in_systm.h"
- #include "../netinet/in_var.h"
- #include "../netinet/ip.h"
- #endif
-
-
- /*
- * Called from boot code to establish sl interfaces.
- */
- slboot(nsl)
- u_long nsl;
- {
- register struct sl_softc *sc;
- register int i;
-
- /* Allocate pointers for sl_softc structures */
- sl_softc = (struct sl_softc **)calloc((nsl+1) * sizeof(u_long));
-
- for (i=0; i < nsl; i++) {
- sl_softc[i] = (struct sl_softc *)calloc(1 * sizeof(struct sl_softc));
- sc = sl_softc[i];
- sc->sc_if.if_name = "sl";
- sc->sc_if.if_unit = i;
- sc->sc_if.if_mtu = SLMTU;
- sc->sc_if.if_flags = IFF_POINTOPOINT;
- sc->sc_if.if_ioctl = slioctl;
- sc->sc_if.if_output = sloutput;
- sc->sc_if.if_snd.ifq_maxlen = ifqmaxlen;
- init_lock(&sc->sc_if.if_snd.ifq_lock, G_NETISR);
- init_lock(&sc->softc_lock, G_NETISR);
- if_attach(&sc->sc_if);
- }
- }
-
- /*
- * Line specific open routine.
- * Attach the given tty to the first available sl unit.
- */
- /* ARGSUSED */
- slopen(dev, tp)
- dev_t dev;
- register struct tty *tp;
- {
- register struct sl_softc *sc;
- register int i;
- spl_t s;
-
- if (!suser())
- return (EPERM);
- if (tp->t_line == SLIPDISC)
- return (EBUSY);
-
- for (i = 0; (sc=sl_softc[i]) != NULL; i++) {
- s = p_lock(&sc->softc_lock, SPLIMP);
- if (sc->sc_ttyp == NULL) {
- sc->sc_flags = 0;
- sc->sc_ilen = 0;
- if (slinit(sc) == 0) {
- v_lock(&sc->softc_lock, s);
- return (ENOBUFS);
- }
- sc->sc_dev = tp->t_dev;
- sc->sc_ttyp = tp;
-
- /* reset interface stats */
- sc->sc_if.if_ipackets = 0;
- sc->sc_if.if_ierrors = 0;
- sc->sc_if.if_opackets = 0;
- sc->sc_if.if_oerrors = 0;
- sc->sc_if.if_collisions = 0;
-
- v_lock(&sc->softc_lock, s);
- ttyflush(tp, FREAD | FWRITE);
- return (0);
- }
- v_lock(&sc->softc_lock, s);
- }
-
- return (ENXIO);
- }
-
- /*
- * Line specific close routine.
- * Detach the tty from the sl unit.
- * Mimics part of ttyclose().
- */
- slclose(tp)
- struct tty *tp;
- {
- register struct sl_softc *sc;
- spl_t s;
-
- ttywflush(tp);
- tp->t_line = 0;
-
- sc = dev_to_sc(tp->t_dev);
- if (sc != NULL) {
- s = p_lock(&sc->softc_lock, SPLIMP);
- if_down(&sc->sc_if);
- sc->sc_ttyp = NULL;
- m_freem(sc->sc_mbuf);
- sc->sc_mbuf = (struct mbuf *)NULL;
- v_lock(&sc->softc_lock, s);
- }
- }
-
- /*
- * Line specific (tty) ioctl routine.
- * Provide a way to get the sl unit number.
- */
- /* ARGSUSED */
- sltioctl(tp, cmd, data, flag)
- struct tty *tp;
- caddr_t data;
- {
- register struct sl_softc *sc;
- spl_t s;
-
- if (cmd == TIOCGETD) {
- sc = dev_to_sc(tp->t_dev);
- s = p_lock(&sc->softc_lock, SPLIMP);
- *(int *)data = sc->sc_if.if_unit;
- v_lock(&sc->softc_lock, s);
- return (0);
- }
- return (-1);
- }
-
- /*
- * Queue a packet. Start transmission if not active.
- */
- sloutput(ifp, m, dst)
- register struct ifnet *ifp;
- register struct mbuf *m;
- struct sockaddr *dst;
- {
- register struct sl_softc *sc;
- spl_t s;
-
- /*
- * `Cannot happen' (see slioctl). Someday we will extend
- * the line protocol to support other address families.
- */
- if (dst->sa_family != AF_INET) {
- printf("sl%d: af%d not supported\n", ifp->if_unit,
- dst->sa_family);
- m_freem(m);
- return (EAFNOSUPPORT);
- }
-
- sc = sl_softc[ifp->if_unit];
- s = p_lock(&sc->softc_lock, SPLIMP);
- if (sc->sc_ttyp == NULL) {
- v_lock(&sc->softc_lock, s);
- m_freem(m);
- return (ENETDOWN); /* sort of */
- }
- if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) {
- v_lock(&sc->softc_lock, s);
- m_freem(m);
- return (EHOSTUNREACH);
- }
- if (IF_QFULL(&ifp->if_snd)) {
- IF_DROP(&ifp->if_snd);
- m_freem(m);
- sc->sc_if.if_oerrors++;
- printf("sl%d: output queue overflow\n", ifp->if_unit);
- v_lock(&sc->softc_lock, s);
- return (ENOBUFS);
- }
- IF_ENQUEUE(&ifp->if_snd, m);
- if ((sc->sc_flags & SC_OACTIVE) == 0) {
- v_lock(&sc->softc_lock, s);
- slstart(sc->sc_ttyp);
- return (0);
- }
- v_lock(&sc->softc_lock, s);
- return (0);
- }
-
- /*
- * Start output on interface. Get another datagram
- * to send from the interface queue and map it to
- * the interface before starting output.
- */
- slstart(tp)
- register struct tty *tp;
- {
- register struct sl_softc *sc;
- register struct mbuf *m;
- register int len;
- register u_char *cp;
- int flush, nd, np, n;
- spl_t s;
- struct mbuf *m2;
- extern int cfreecount;
-
- sc = dev_to_sc(tp->t_dev);
- for (;;) {
- /*
- * If there is more in the output queue, just send it now.
- * We are being called in lieu of ttstart and must do what
- * it would.
- */
- if (tp->t_outq.c_cc > 0)
- ttstart(tp);
- if (tp->t_outq.c_cc > SLIP_HIWAT)
- return;
-
- /*
- * This happens briefly when the line shuts down.
- */
- if (sc == NULL)
- return;
-
-
- s = p_lock(&sc->softc_lock, SPLIMP);
-
- /*
- * If system is getting low on clists
- * and we have something running already, stop here.
- */
- if (cfreecount < CLISTRESERVE + SLMTU &&
- sc->sc_flags & SC_OACTIVE) {
- v_lock(&sc->softc_lock, s);
- return;
- }
-
- /*
- * Get a packet and send it to the interface.
- */
- IF_DEQUEUE(&sc->sc_if.if_snd, m);
- if (m == NULL) {
- if (tp->t_outq.c_cc == 0)
- sc->sc_flags &= ~SC_OACTIVE;
- v_lock(&sc->softc_lock, s);
- return;
- }
- flush = !(sc->sc_flags & SC_OACTIVE);
- sc->sc_flags |= SC_OACTIVE;
- v_lock(&sc->softc_lock, s);
-
- /*
- * The extra FRAME_END will start up a new packet, and thus
- * will flush any accumulated garbage. We do this whenever
- * the line may have been idle for some time.
- */
- if (flush)
- (void) putc(FRAME_END, &tp->t_outq);
-
- while (m) {
- cp = mtod(m, u_char *);
- len = m->m_len;
- while (len > 0) {
- /*
- * Find out how many bytes in the string we can
- * handle without doing something special.
- */
- nd = locc(FRAME_ESCAPE, len, cp);
- np = locc(FRAME_END, len, cp);
- n = len - MAX(nd, np);
- if (n) {
- /*
- * Put n characters at once
- * into the tty output queue.
- */
- if (b_to_q((char *)cp, n, &tp->t_outq))
- break;
- len -= n;
- cp += n;
- }
- /*
- * If there are characters left in the mbuf,
- * the first one must be special..
- * Put it out in a different form.
- */
- if (len) {
- if (putc(FRAME_ESCAPE, &tp->t_outq))
- break;
- if (putc(*cp == FRAME_ESCAPE ?
- TRANS_FRAME_ESCAPE : TRANS_FRAME_END,
- &tp->t_outq)) {
- (void) unputc(&tp->t_outq);
- break;
- }
- cp++;
- len--;
- }
- }
- MFREE(m, m2);
- m = m2;
- }
-
- if (putc(FRAME_END, &tp->t_outq)) {
- /*
- * Not enough room. Remove a char to make room
- * and end the packet normally.
- * If you get many collisions (more than one or two
- * a day) you probably do not have enough clists
- * and you should increase "nclist" in param.c.
- */
- (void) unputc(&tp->t_outq);
- (void) putc(FRAME_END, &tp->t_outq);
- s = p_lock(&sc->softc_lock, SPLIMP);
- sc->sc_if.if_collisions++;
- printf("sl%d: clist overflow\n", sc->sc_if.if_unit);
- v_lock(&sc->softc_lock, s);
- } else {
- s = p_lock(&sc->softc_lock, SPLIMP);
- sc->sc_if.if_opackets++;
- v_lock(&sc->softc_lock, s);
- }
- }
- }
-
- slinit(sc)
- register struct sl_softc *sc;
- {
- struct mbuf *m;
-
- if (sc->sc_mbuf == (struct mbuf *)NULL) {
- m = m_getcl(M_DONTWAIT, MT_DATA);
- if (m) {
- m->m_len = MCLEN;
- sc->sc_mbuf = m;
- sc->sc_mp = mtod(m, char *);
- } else {
- printf("sl%d: can't allocate mbuf cluster\n",sc->sc_if.if_unit);
- sc->sc_if.if_flags &= ~IFF_UP;
- return (0);
- }
- }
- return (1);
- }
-
- /*
- * Copy data buffer to mbuf chain; add ifnet pointer ifp.
- */
- struct mbuf *
- sl_btom(bufp, count, ifp)
- register u_char *bufp;
- register int count;
- struct ifnet *ifp;
- {
- register struct mbuf *m;
- struct mbuf *mhead;
-
- m = m_getm(M_DONTWAIT, MT_DATA,
- howmany((count+sizeof(struct ifnet*)), MLEN));
-
- if ((mhead = m) != (struct mbuf *)NULL) {
- mhead->m_off += sizeof(struct ifnet *);
- m->m_len = (count > IFPMLEN) ? IFPMLEN : count;
- bcopy((caddr_t)bufp, mtod(m, caddr_t), (unsigned)m->m_len);
- count -= m->m_len;
- bufp += m->m_len;
- m = m->m_next;
-
- while (count) {
- m->m_off = MMINOFF;
- m->m_len = (count > MLEN) ? MLEN : count;
- bcopy((caddr_t)bufp, mtod(m, caddr_t),
- (unsigned)m->m_len);
- count -= m->m_len;
- bufp += m->m_len;
- m = m->m_next;
- }
-
- /* Copy in ifnet struct pointer info */
- mhead->m_off -= sizeof(struct ifnet *);
- mhead->m_len += sizeof(struct ifnet *);
- *mtod(mhead, struct ifnet **) = ifp;
- }
-
- return(mhead);
- }
-
-
- /*
- * tty interface receiver interrupt.
- */
- slinput(c, tp)
- register int c;
- register struct tty *tp;
- {
- register struct sl_softc *sc;
- register struct mbuf *m;
- spl_t s;
-
-
- l.cnt.v_ttyin++;
- sc = dev_to_sc(tp->t_dev);
- if (sc == NULL)
- return;
-
- c &= 0xff;
- s = p_lock(&sc->softc_lock, SPLIMP);
- if (sc->sc_flags & SC_ESCAPED) {
- sc->sc_flags &= ~SC_ESCAPED;
- switch (c) {
-
- case TRANS_FRAME_ESCAPE:
- c = FRAME_ESCAPE;
- break;
-
- case TRANS_FRAME_END:
- c = FRAME_END;
- break;
-
- default:
- sc->sc_if.if_ierrors++;
- printf("sl%d: framing error\n", sc->sc_if.if_unit);
- sc->sc_mp = mtod(sc->sc_mbuf, char *);
- sc->sc_ilen = 0;
- v_lock(&sc->softc_lock, s);
- return;
- }
- } else {
- switch (c) {
-
- case FRAME_END:
- if (sc->sc_ilen == 0){ /* ignore */
- v_lock(&sc->softc_lock, s);
- return;
- }
- m = sl_btom( mtod(sc->sc_mbuf, u_char *), sc->sc_ilen,
- &sc->sc_if );
- if (m == (struct mbuf *)NULL) {
- sc->sc_if.if_ierrors++;
- printf("sl%d: out of memory\n", sc->sc_if.if_unit);
- v_lock(&sc->softc_lock, s);
- return;
- }
- sc->sc_mp = mtod(sc->sc_mbuf, char *);
- sc->sc_ilen = 0;
- sc->sc_if.if_ipackets++;
- if (IF_QFULL(&ipintrq)) {
- IF_DROP(&ipintrq);
- sc->sc_if.if_ierrors++;
- printf("sl%d: input queue overflow\n", sc->sc_if.if_unit);
- v_lock(&sc->softc_lock, s);
- m_freem(m);
- } else {
- IF_ENQUEUE(&ipintrq, m);
- v_lock(&sc->softc_lock, s);
- schednetisr(NETISR_IP);
- }
- return;
-
- case FRAME_ESCAPE:
- sc->sc_flags |= SC_ESCAPED;
- v_lock(&sc->softc_lock, s);
- return;
- }
- }
- if (++sc->sc_ilen > SLMTU) {
- sc->sc_if.if_ierrors++;
- printf("sl%d: frame overflow\n", sc->sc_if.if_unit);
- sc->sc_mp = mtod(sc->sc_mbuf, char *);
- sc->sc_ilen = 0;
- v_lock(&sc->softc_lock, s);
- return;
- }
- *sc->sc_mp++ = c;
- v_lock(&sc->softc_lock, s);
- }
-
- /*
- * Process an ioctl request.
- */
- slioctl(ifp, cmd, data)
- register struct ifnet *ifp;
- int cmd;
- caddr_t data;
- {
- register struct ifaddr *ifa = (struct ifaddr *)data;
- spl_t s;
- int error = 0;
-
- s = p_lock(&ifp->if_snd.ifq_lock, SPLIMP);
- switch (cmd) {
-
- case SIOCSIFADDR:
- if (ifa->ifa_addr.sa_family == AF_INET)
- ifp->if_flags |= IFF_UP;
- else
- error = EAFNOSUPPORT;
- break;
-
- case SIOCSIFDSTADDR:
- if (ifa->ifa_addr.sa_family != AF_INET)
- error = EAFNOSUPPORT;
- break;
-
- default:
- error = EINVAL;
- }
- v_lock(&ifp->if_snd.ifq_lock, s);
- return (error);
- }
-
- /*
- * locate mask
- */
- locc(mask, size, cp)
- register u_char mask;
- u_int size;
- register u_char *cp;
- {
- register u_char *end = &cp[size];
-
- while (cp < end && *cp != mask)
- cp++;
- return (end - cp);
- }
-
- /*
- * locate sl_softc for device
- */
- struct sl_softc *
- dev_to_sc( dev )
- register dev_t dev;
- {
- register int i;
-
- for (i=0; sl_softc[i] != NULL; i++)
- if (sl_softc[i]->sc_dev == dev)
- return(sl_softc[i]);
- return(NULL);
- }
-